//
// Created by 原帅 on 2022/10/3.
//

#ifndef R_RMEMORYMANGER_H
#define R_RMEMORYMANGER_H

#include "unistd.h"
#include "RMemory.h"
#include <map>
#include "sys/shm.h"
#include "errno.h"
#include "err.h"
#include "stdint.h"
#include "RLog.h"
#include "string.h"

using namespace std;

/**
 * 不支持深copy
 */
class RMemoryManger {
public:
    static RMemoryManger &Inst() {
        static RMemoryManger rsmManger;
        return rsmManger;
    }

    map<int, RMemObj *> rMemoryPool;

    template<class T>
    RMemory<T>* createRMemory(int key, uint64_t num) {
        uint64_t unitSize = sizeof(T);
        uint64_t totalSize = num * unitSize + sizeof(RMemoryInfo);
        RMemory<T> *t = getRMemoryWithKey<T>(key);
        if (t) {
            if (t->getRMemoryInfo() != nullptr)
            {
                if (t->getRMemoryInfo()->totalcount == num && t->getRMemoryInfo()->memsize == totalSize) {
                    return t;
                }
                else {
                    RLogErr("RMemory already exists! but you are try to recreate it with mismatch size!");
                    return nullptr;
                }
            } else {
                RLogErr("RMemory already exists! but it seems is not shared in memory!");
                return nullptr;
            }

        }
        t = new RMemory<T>();
        if (t == nullptr) return nullptr;

        int iRet = shmget(key, totalSize, IPC_CREAT | IPC_EXCL | 0666);
        if (iRet != -1) {
            void *addr = shmat(iRet, NULL, 0);
            if (addr && addr != (void *) -1) {
                RMemoryInfo *rMemoryInfo = (RMemoryInfo *) addr;
                rMemoryInfo->memsize = totalSize;
                rMemoryInfo->totalcount = num;
                rMemoryInfo->curnullptr = 0;
                t->initialize(key, iRet, addr);
                rMemoryPool.insert(pair<int, RMemObj *>(key, t));
                return t;
            } else {
                RLogErr("shmat error:%s",strerror(errno));
                if (t) delete t;
                return nullptr;
            }
        } else {
            if (errno == EEXIST) {
                iRet = shmget(key, num * unitSize + sizeof(RMemoryInfo), IPC_CREAT);
                if (-1 != iRet) {
                    void *addr = shmat(iRet, NULL, 0);
                    if (addr) {
                        RMemoryInfo *rMemoryInfo = (RMemoryInfo *) addr;
                        if (rMemoryInfo->memsize == totalSize && rMemoryInfo->totalcount ==num)
                        {
                            t->initialize(key, iRet, addr);
                            rMemoryPool.insert(pair<int, RMemObj *>(key, t));
                            return t;
                        }
                        else
                        {
                            RLogErr("RMemory already exists! but struct is mismatch!");
                            if (t) delete t;
                            return nullptr;
                        }
                    } else {
                        if (t) delete t;
                        return nullptr;
                    }
                }
            }
            if (errno == EINVAL) {
                RLogErr("It seems RMemory already exists，but created size is mismatch",strerror(errno));
                if (t) delete t;
                return nullptr;
            }
            /// else
            {
                RLogErr("shmat error:%s",strerror(errno));
                if (t) delete t;
                return nullptr;
            }
        }
        if (t) delete t;
        return nullptr;
    }

    template<class T>
    RMemory<T> *getRMemoryWithKey(int key) {
        if (rMemoryPool.count(key)) {
            RMemObj *rMemObj = rMemoryPool.at(key);
            return (RMemory<T> *) rMemObj;
        }
        return nullptr;
    }

};


#endif //R_RMEMORYMANGER_H
